package com.bzg.spring.application;

import com.bzg.spring.annotation.MyAutowired;
import com.bzg.spring.myenum.Scope;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;

/**
 * 容器工厂
 * */
public class MyBeanFactory implements BeanFactory,BeanDefinitionRegistrar{

	private Map<String,Object> singletonObjects = new HashMap();

	private Map<String,MyBeanDefinition> beanDefinitionMap = new HashMap<>();

	@Override
	public Object getBean(String beanName) {
		Object bean = singletonObjects.get(beanName);
		if(bean != null) {
			return bean;
		}
		MyBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
		if(beanDefinition == null) {
			return null;
		}
		if(beanDefinition.getScope().equals(Scope.PROTOTYPE.getValue())) {
			return createBean(beanName, beanDefinition);
		}else {
			Object o = createBean(beanName, beanDefinition);
			singletonObjects.put(beanName,o);
			return o;
		}
	}

	@Override
	public void registrarBean(String beanName, Object bean) {
        singletonObjects.put(beanName,bean);
	}

	@Override
	public Object getBean(Class<?> typeClass) {
		List<MyBeanDefinition> definitionList = lookDefinitionByType(typeClass,true);
		if(definitionList.size() == 0) {
			throw new RuntimeException("不存在此类型bean：" + typeClass.getName());
		}
		if(definitionList.size() > 1) {
			throw new RuntimeException("存在多个此类型bean：" + typeClass.getName());
		}
		return getBean(definitionList.get(0).getBeanName());
	}

	@Override
	public Map<String, Object> getBeans(Class<?> typeClass) {
		List<MyBeanDefinition> definitionList = lookDefinitionByType(typeClass,true);
		if(definitionList.size() == 0) {
			return new HashMap<>();
		}
		Map<String,Object> map = new HashMap();
		definitionList.forEach(v -> {
			Object bean = getBean(v.getBeanName());
			map.put(v.getBeanName(),bean);
		});
		return map;
	}

	private List<MyBeanDefinition> lookDefinitionByType(Class<?> typeClass, boolean isFilter) {
		List<MyBeanDefinition> list = new ArrayList();
		beanDefinitionMap.forEach((k, v) -> {
			if (v.getBeanClass() != null && typeClass.isAssignableFrom(v.getBeanClass())) {
				if (isFilter) {
					if (v.getBeanClass().isInterface() || Modifier.isAbstract(v.getBeanClass().getModifiers())) {
						return;
					}
				}
				list.add(v);
			}
		});
		return list;
	}

	@Override
	public void registrarBeanDefinition(String beanName, MyBeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName,beanDefinition);
	}

	@Override
	public Map<String, MyBeanDefinition> getBeanDefinition() {
		return beanDefinitionMap;
	}

	private Object createBean(String beanName,MyBeanDefinition beanDefinition){
		Object bean;
		try {
			Constructor<?> constructor = beanDefinition.getBeanClass().getConstructor();
			bean = constructor.newInstance();
		}catch (Exception e) {
			throw new RuntimeException("无默认构造函数：" + beanDefinition.getBeanClass().getName());
		}
		// 属性注入
		populate(bean,beanDefinition.getBeanClass());

		Object result;
		// 初始化前
		result = applyBeanPostProcessor(bean,(MyBeanPostProcessor k) -> k.postProcessBeforeInitialization(bean,beanName));

		// 初始化
		if(bean instanceof MyInitializingBean) {
			((MyInitializingBean) bean).afterPropertiesSet();
		}

		// 初始化后
		return applyBeanPostProcessor(bean,(MyBeanPostProcessor k) -> k.postProcessAfterInitialization(result,beanName));
	}

	// 执行bean后置处理器的处理方法（这里为了代码重用，利用了函数接口）
	private Object applyBeanPostProcessor(Object bean,Function<MyBeanPostProcessor,Object> action) {
		if(bean instanceof MyBeanPostProcessor) {
			return bean;
		}
		// 注意：这里如果bean后置处理器执行会栈溢出，因为bean后置处理器在执行过程中也经历了这一步，使得其产生循环。所以这里我们排除对BeanPostProcessor的处理
		// spring并非这样处理，spring是用一个集合存储了所有的BeanPostProcessor，而并不是像我们这样去容器中获取
		// 刚开始时spring的BeanPostProcessor集合是空的，所以所有后置处理器都不会被后置处理。所有后置处理器创建完成后
		// 才放入这个集合
		Map<String, Object> beans = getBeans(MyBeanPostProcessor.class);
		Collection<Object> values = beans.values();
		for (Object value : values) {
			Object apply = action.apply((MyBeanPostProcessor) value) ;
			if(apply == null) {   // 后置处理器在返回为null时结束后置处理器的处理
				return bean;
			}
			bean = apply;
		}
		return bean;
	}

	/**
	 * 不支持父类及接口(仅支持字段注入)
	 * spring Autowired注解的注入逻辑：
	 * 根据类型找bean，如果bean只有一个那么直接注入，如果没有，判断是否必须，必须则报错否则不进行注入
	 * 如果发现多个此类型bean，则根据字段或参数名称匹配，匹配到就使用。否则判断是否必须，必须则报错否则不进行注入
	 * */
	private void populate(Object bean,Class<?> beanClass){
		if(bean != null) {
			Field[] fields = beanClass.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				Field field = fields[i];
				field.setAccessible(true);
				MyAutowired annotation = field.getAnnotation(MyAutowired.class);
				if(annotation != null) {
					Map<String, Object> beans = getBeans(field.getType());
					if(beans.size() == 0) {
						if(annotation.required()) {
							throw new RuntimeException("无此类型bean可以注入：" + field.getType());
						}
						return;
					}
					if(beans.size() == 1) {
						beans.entrySet().forEach(f -> {
							try {
								field.set(bean,f.getValue());
							} catch (IllegalAccessException e) {
								e.printStackTrace();
							}
						});
					}else {
						beans.entrySet().forEach(f -> {
							String name = field.getName();
							if(name.equals(f.getKey())) {
								try {
									field.set(bean, f.getValue());
									return;
								} catch (IllegalAccessException e) {
									e.printStackTrace();
								}
							}
							if(annotation.required()) {
								throw new RuntimeException("发现多个此类型bean：" + field.getType());
							}
						});
					}
				}
			}
		}
	}
}
